var mineQuantityPos = {
    1 : 13,
    2 : 17,
    3 : 21,
    4 : 29,
    5 : 29
};

function getMineLevel(userLevel) {
    var mineLevel = 0;
    for (var level in gConfMineDistribute) {
        if (userLevel < level) break;
        mineLevel = level;
    }
    return mineLevel;
}

function Mine(){
    this.mines = {};
    this.leaves = {};/* uid : {
                            {gold:, enemy:, time:}
                        }
                    */
    this.dirtyCount = 0;
    this.updates = {};
}

Mine.create = function() {
    var initMine = {};
    // 初始化金矿,等级-编号-矿位置
    for (var level in gConfMineDistribute) {
        var mineCount = 10;

        initMine[level] = {};

        for( var id=1; id<=mineCount; id++ ) {
            var posCount = id > 5 ? mineQuantityPos[5] : mineQuantityPos[id];
            initMine[level][id] = {};
            for( var pos=1; pos<=posCount; pos++ ) {
                initMine[level][id][pos] = {
                    owner: 0,   // 占领者ID(没有为0)
                    timer: 0,   // 占领时间
                    output: 0   // 赔偿产量
                };
            }
        }
    }
    gDBWorld.insert({_id:'mine', mine:initMine}, function(err, result){}); 
};

Mine.prototype = {
    init : function(callback) {

        // 读取金矿数据
        gDBWorld.findOne({_id : 'mine'}, {}, function(err, doc){
            if( doc ) {
                this.mines = doc.mine;
                this._init();
                callback && callback(true);
            }else{
                callback && callback(false);
            }
        }.bind(this));
    },

    _init : function() {
        var updates = {};
        for( var level in gConfMineDistribute ) {
            if (this.mines[level]) continue;

            var mineCount = 10;
            var initMine = {};

            for( var id=1; id<=mineCount; id++ ) {
                var posCount = id > 5 ? mineQuantityPos[5] : mineQuantityPos[id];
                initMine[id] = {};
                for( var pos=1; pos<=posCount; pos++ ) {
                    initMine[id][pos] = {
                        owner: 0,   // 占领者ID(没有为0)
                        timer: 0,   // 占领时间
                        output: 0   // 赔偿产量
                    };
                }
            }

            this.mines[level] = initMine;
            updates["mine."+level] = initMine;
        }

        if( Object.keys(updates).length > 0 ) {
            gDBWorld.update({_id : 'mine'}, {$set:updates}, function(err, result){
                if( err ) {
                    ERROR(util.format('SAVE MINE: %j %j', updates, err));
                }else {
                    LOG(util.format('SAVE MINE: %j', updates));
                }
            });
        }
    },

    getOutput : function(userLevel, mineLevel, id, pos, _uid){
        var mine = this.mines[mineLevel][id][pos];
        var mineConf = gConfMine[Math.min(id, 5)];

        var outputFactor = gAssistMine.getOutput(_uid, 0);

        var outputPerHour = mineConf.OutputPerHour * gConfLevel[userLevel].MineOutputFactor;
        var outputTime = Math.min(gConfGlobal.MineOccupyTime*3600, common.getTime() - mine.timer);

        var allOutput = Math.floor((mine.output + Math.floor(outputTime/3600 * outputPerHour)) * outputFactor);
        var maxOutput = Math.floor(gConfGlobal.MineMaxOutput * outputPerHour);
       
        return Math.min(allOutput, maxOutput);
    },

    isFull : function(userLevel, mineLevel, id, pos) {
        var mine = this.mines[mineLevel][id][pos];
        var mineConf = gConfMine[Math.min(id, 5)];

        var outputPerHour = mineConf.OutputPerHour * gConfLevel[userLevel].MineOutputFactor;
        var outputTime = Math.min(gConfGlobal.MineOccupyTime*3600, common.getTime() - mine.timer);

        var allOutput = mine.output + Math.floor(outputTime/3600 * outputPerHour);
        var maxOutput = Math.floor(gConfGlobal.MineMaxOutput * outputPerHour);
        
        return (allOutput >= maxOutput) ? true : false;
    },

    getWarCost : function(mineLevel, id, pos) {
        var mine = this.mines[mineLevel][id][pos];
        var mineConf = gConfMine[Math.min(id, 5)];

        var outputPerHour = mineConf.OutputPerHour * gConfMineDistribute[mineLevel].CostFactor;
        var outputTime = Math.min(gConfGlobal.MineOccupyTime*3600, common.getTime() - mine.timer);

        var allOutput = mine.output + Math.floor(outputTime/3600 * outputPerHour);
        var maxOutput = Math.floor(outputPerHour * gConfGlobal.MineMaxOutput);
        
        return Math.floor(Math.min(allOutput, maxOutput)*mineConf.Cost/100);
    },

    markMineDirty : function(level, id, pos, mine){
        delete mine.name;
        delete mine.headpic;

        this.updates[util.format('mine.%d.%d.%d', level, id, pos)] = mine;
        this.dirtyCount += 1;
    },

    save : function(force, callback) {
        if( !force && (this.dirtyCount < 10) ) {
            callback && callback(true);
            return;
        }

        var updates = this.updates;
        this.updates = {};
        this.dirtyCount = 0;

        gDBWorld.update({_id : 'mine'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR(util.format('SAVE MINE: %j %j', updates, err));
                callback && callback(false);
            }else {
                LOG(util.format('SAVE MINE: %j', updates));
                callback && callback(true);
            }
        });
    },

    saveLeave : function(uid) {
        var leave = this.leaves[uid] || 0;
        gDBWorld.save({_id:'mine_leave_'+uid, 'leave':leave}, function(err, result){});
    },

    getLeave : function(uid, callback){
        if( uid in this.leaves ) {
            callback && callback(this.leaves[uid]);
        }else{
            gDBWorld.findOne({_id:'mine_leave_'+uid,}, {}, function(err, result){
                var leave = result ? result.leave : 0;
                this.leaves[uid] = leave;
                callback && callback(leave);
            }.bind(this));
        }
    },

    update : function(level, id) {
        var now = common.getTime();
        var maxTime = gConfGlobal.MineOccupyTime * 3600;
        
        var mines = this.mines[level][id];
        for( var pos in mines ) {
            var mine = mines[pos];
            if( !mine.owner || (mine.timer+maxTime) > now ) {
                continue;
            }

            var ownerInfo = gUserInfo.getUser(mine.owner); 
            if (ownerInfo) {
                var output = gMine.getOutput(ownerInfo.level, level, id, pos, mine.owner);
                this.leaves[mine.owner] = {gold:output, enemy:0, time:common.getTime()};
                this.saveLeave(mine.owner);
            }

            mine.owner = 0;
            mine.timer = 0;
            mine.output = 0;

            this.markMineDirty(level, id, pos, mine);
        }
    },
};

function occupy(occupied, uid, owner, mine, mineInfo, resp, battleReport){
    var playerInfo = gUserInfo.getUser(uid);
    var ownerInfo = gUserInfo.getUser(owner);

    var goldCost = 0;
    var ownerGold = 0;

    var attackTime = common.getTime();     
    if (owner) {
        var currentOutput = gMine.getOutput(ownerInfo.level, mineInfo.level, mineInfo.id, mineInfo.pos, owner);
        var warCost = gMine.getWarCost(mineInfo.level, mineInfo.id, mineInfo.pos);

        var isFull = 0;
        if (!occupied) {
            isFull = gMine.isFull(ownerInfo.level, mineInfo.level, mineInfo.id, mineInfo.pos);
            if (!isFull) {
                mine.output += warCost;
            }

            goldCost = -warCost;
            ownerGold = warCost;
        }else{
            goldCost = warCost;
            ownerGold = currentOutput - warCost;
        }

        gMessage.addMessage(owner,
            {
                'uid' : uid,
                'name' : playerInfo.name,
                'time' : attackTime,
                'atkgold' : goldCost,
                'defgold' : ownerGold,
                'succ' : occupied,
                'replay' : gReplay.addReplay(battleReport),
                'type' : 'mine',
                'flag' : 0,
                'full' : isFull,
            }); 
        
        gMessage.addMessage(uid,
            {
                'uid' : owner,
                'name' : ownerInfo.name,
                'time' : attackTime,
                'atkgold' : goldCost,
                'defgold' : ownerGold,
                'succ' : occupied,
                'replay' : gReplay.addReplay(battleReport),
                'type' : 'mine',
                'flag' : 1,
                'full' : isFull,
            }); 
    }
    
    if (occupied) {
        if (owner) {
            var leave = {
                enemy: uid, 
                time: attackTime, 
                gold: ownerGold
            };

            gMine.leaves[owner] = leave;
            gMine.saveLeave(owner);

            // 矿被抢了
            gAssistMine.unMarkMine(owner, 0, null);
        }

        mine.owner = uid;
        mine.timer = attackTime;
        mine.output = 0;
    }

    gMine.markMineDirty(mineInfo.level, mineInfo.id, mineInfo.pos, mine);

    var mine = clone(mine);
    var ownerInfo = gUserInfo.getUser(mine.owner);
    mine.name = ownerInfo.name;
    mine.headpic = ownerInfo.headpic;
    mine.league = ownerInfo.league;

    resp.data.mine = mine;
    resp.data.occupied = occupied;
    resp.data.gold = goldCost;
    resp.data.level = mineInfo.level;
    resp.data.id = mineInfo.id;
    resp.data.pos = mineInfo.pos;

    gMine.save();
}

exports.get = function(req, resp, onHandled) {
    var uid = req.uid; 
    var occupying = req.args.occupying;
    var userLevel = req.args.level;
    var level = getMineLevel(userLevel);
    var id = 1;
    do {
        if( !isNaN(req.args.id) ) {
            // 读取指定金矿
            id = +req.args.id;
        }else if( occupying ) {
            // 读取自己占领金矿
            level = occupying.level;
            id = occupying.id;
        }

        if( !(level in gMine.mines) || !(id in gMine.mines[level]) ) {
            resp.code = 1; resp.desc = 'level, id not exist in mine'; break;
        }

        gMine.update(level, id);
        gMine.save();
    }while(false);

    if( resp.code != 0 ) {
        onHandled();
        return;
    }

    gMine.getLeave(uid, function(leave){
        var mineLost = false;
        var addGold = 0;
        if( leave ) {
            mineLost = true;
            addGold = leave.gold;
            gMine.leaves[uid] = 0;
            gMine.saveLeave(uid);
            
            if( leave.enemy) {
                resp.data.leave = gUserInfo.getUser(leave.enemy).name;
            }
        }else if( occupying ) {
            if( gMine.mines[occupying.level][occupying.id][occupying.pos].owner != uid ) {
                mineLost = true;
            }
        }
        
        var mines = clone(gMine.mines[level][id]);
        var leagues = {};
        for( var pos in mines ) {
            var mine = mines[pos];
            var ownerUid = mine.owner;
            if( ownerUid ) {
                var userInfo = gUserInfo.getUser(ownerUid);
                leagues[pos] =  userInfo.league;
                mine.name = userInfo.name;
                mine.headpic = userInfo.headpic;
            }
        }

        resp.data.lost = mineLost;
        resp.data.gold = addGold; 
        resp.data.mines = mines; 
        resp.data.leagues = leagues;

        onHandled();
    });
}

exports.occupy = function(req, resp, onHandled) {
    var mine, mineInfo;
    do {
        var id = req.args.id;
        var pos = req.args.pos;
        // 是否为占矿操作 
        var doOccupy = req.args.occupy ? 1 : 0;

        var havegold = +req.args.havegold;

        var userLevel = req.args.level;
        var level = getMineLevel(userLevel);

        if( level <= 0 ) {
            resp.code = 1; resp.desc = 'invalid level'; break;
        }

        if( !(level in gMine.mines) || !(id in gMine.mines[level]) ) {
            resp.code = 1; resp.desc = 'level, id not exist in mine'; break;
        }

        mine = gMine.mines[level][id][pos];
        if( !mine ) {
            resp.code = 1; resp.desc = 'pos not in mine'; break;
        }

        if( doOccupy && mine.owner ) {
            resp.data.already = 1; 
            break;
        }

        var sheldTime = 58;
        if( mine.owner && (common.getTime() - mine.timer) < sheldTime ) {
            resp.code = 1; resp.desc = 'mine in sheld'; break;
        }

        mineInfo = {level: level, id: id, pos: pos};

        if( !mine.owner ) {
            occupy(true, req.uid, null, mine, mineInfo, resp);
            break;
        }

        // PVP 抢夺
        var warCost = gMine.getWarCost(level, id, pos);
        if (havegold < warCost) {
            resp.code = 1; resp.desc = 'not enough gold'; break;
        }

        doPVP(req.uid, mine.owner, function(battleReport) {
            if( !battleReport ) {
                resp.code = 1; 
                resp.desc = 'pvp error';
                onHandled();
                return;
            }

            resp.data.battle = battleReport;
            occupy(battleReport.success, req.uid, mine.owner, mine, mineInfo, resp, battleReport);
            onHandled();
        });
        return;
    }while(false);

    onHandled();
}

exports.leave = function(req, resp, onHandled) {
    do {
        var occupying = req.args.occupying;
        var id = occupying.id;
        var pos = occupying.pos;
        var userLevel = req.args.level;
        var level = getMineLevel(userLevel);

        if( occupying ) {
            // 读取自己占领金矿
            level = occupying.level;
        }

        var mine = gMine.mines[level][id][pos];

        var gold = 0;
        if( mine && mine.owner == req.uid) {
            gold = gMine.getOutput(userLevel, level, id, pos, mine.owner);

            mine.owner = 0;
            mine.timer = 0;
            mine.output = 0;

            gMine.markMineDirty(level, id, pos, mine);
        }

        resp.data.gold = gold;
        resp.data.mine = mine;
        resp.data.level = level;
        resp.data.id = id;
        resp.data.pos = pos;
    }while(false);

    gMine.save();
    onHandled();
}

exports.Mine = Mine;
